/*=============================================================================
#    The element-comm is a tentative program used to test our ideas.           
#                       Copyright (C)  bgao                                    
#
#     This program is free software; you can redistribute it and/or            
#      modify it under the terms of the GNU General Public License             
#     as published by the Free Software Foundation; either version 2           
#         of the License, or (at your option) any later version.               
#
#     This program is distributed in the hope that it will be useful,          
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            
#             GNU General Public License for more details.                    
#
#    You should have received a copy of the GNU General Public License        
#     along with this program; if not, write to the Free Software             
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,             
#                       MA  02110-1301, USA.                                  
#==============================================================================
# $Id: mac_scan.c 348 2010-11-29 09:01:16Z bgao $ 
#
# $Author: bgao $ 
#
# $Revision: 348 $ 
#
# $Date: 2010-11-29 17:01:16 +0800 (星期一, 29 十一月 2010) $ 
#
# Description: 
#
=============================================================================*/
/*! @file mac_scan.c
 *  @brief This file implements the MAC channel scanning mechanism.
 */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "mac.h"


/*! the size of the energy buffer */
#define ENERGY_BUF_SIZE 200

/*! the structure used to assist the active scan */
typedef struct active_scan_assist_t_ {
    mac_scan_chnls_t scan_chnls;     /* the number of channels to be scanned */
    uint8 chnls[NUM_OF_CHNLS_SUP];   /* the location used to stored energy value for each channel */
    uint8 duration;                  /* the duration of each scan */
    uint8 chnl_index;                /* the index of the each channel */
    uint16 pan_id_stored;            /* the pan_id before scan */
} active_scan_assist_t;

/* the instance of the active scan assistant */
static active_scan_assist_t asa;

static int8 search_biggest(int8 *buf, uint8 num);
static int8 delay(uint16 t);
static void active_scan_callback(int8 signum);
static int8 active_scan_engine(void);

/*! mac scan request interface.
 *  @param req pointer to the structure of scan request.
 *  @return return the status of scan request.
 */
int8 mac_scan_req(mac_scan_req_t *req)
{
    mac_state_t state;
    mac_scan_cfm_t cfm;
    int8 r;

    if (req == NULL) {
        return MAC_ERR;
    }

    state = mac_get_state();
    if (state == mac_scanning) {
        r = SCAN_IN_PROGRESS;
        goto scan_req_err_process;
    }

    switch (req->scan_type) {
        case mac_ed_scan_type:
            return mac_ed_scan(req);
            
        case mac_active_scan_type:
            return mac_active_scan(req);

        case mac_passive_scan_type:
            return mac_passive_scan(req);

        case mac_orphan_scan_type:
            return mac_orphan_scan(req);

        default:
            goto scan_req_err_process;
    }
scan_req_err_process:
    /* TODO confirm */
    cfm.status = r;
    mac_scan_cfm(&cfm);

    return r;
}

/*! energy dectect scan interface.
 *  @param req pointer to the strucure of energy scan.
 *  @return return the status of the energy scan.
 */
int8 mac_ed_scan(mac_scan_req_t *req)
{
    uint8 i;
    uint8 j;
    uint32 t;
    int8 r;
    int16 duration = aBaseSuperframeDuration * ((1 << req->scan_duration) + 1);
    int8 energy_buf[ENERGY_BUF_SIZE];
    int8 peak;
    mac_scan_chnls_t *scan_chnls = &req->scan_chnls;
    mac_scan_cfm_t cfm;
    int8 result_list[NUM_OF_CHNLS_SUP];
    int8 result_size = 0;

    cfm.scan_type = mac_ed_scan_type;
    if (req == NULL) {
        r = MAC_INVALID_ADDRESS;
        goto ed_scan_err_process;
    }

    mac_set_state(mac_ed_scanning);
    for (i = 0; i < scan_chnls->num; i++) {
        memset(energy_buf, 0x80, ENERGY_BUF_SIZE);
        rf_set_chnl(*(scan_chnls->chnls+i));
        /* delay for a while to wait the RSSI value to be valid */
        delay(10);
        /* energy scan */ 
        /* TODO start timer, which will time out aBaseSuperframeDuration * (2**n + 1) symbols later */
        t = timer_get();
        for (j = 0; j < ENERGY_BUF_SIZE; j++) {
            /* TODO delay for a symbol time to wait the RSSI value to be updated */
            delay(2);
            energy_buf[j] = rf_ed_scan();
            /* check the timer to see if it times out, if it does, break */
            if ((int16)(timer_get() - t) > duration) {
                break;
            }
        }
        /* search the energy_buf to find the peak(biggest) energy value. */
        peak = search_biggest(energy_buf, ENERGY_BUF_SIZE);
        result_list[i] = peak;
        result_size++;
        MAC_DEBUG("mac_ed_scan: peak value is %d\n", peak);
    }

    cfm.status = SUCCESS;
    cfm.result_list_size = result_size;
    cfm.energy_detect_list = result_list;
    cfm.pan_desc_list = NULL;
    cfm.scan_chnls = req->scan_chnls;
    mac_scan_cfm(&cfm);

    mac_set_state(mac_idle);

    return MAC_OK;

ed_scan_err_process:
    /* TODO cofirm */
    cfm.status = r;
    cfm.energy_detect_list = NULL;
    cfm.result_list_size = 0;
    cfm.pan_desc_list = NULL;
    cfm.scan_chnls = req->scan_chnls;
    mac_scan_cfm(&cfm);
    
    mac_set_state(mac_idle);

    return r;
}

/*! local function use to find the biggest value in a buf pointed by buf, which has
 *  num number of elements.
 *  @param buf pointer to the structure of value buffer.
 *  @param num number of elements in the buffer.
 *  @return the biggest value in the buffer.
 */
static int8 search_biggest(int8 *buf, uint8 num)
{
    int8 b;
    uint8 i;

    if (buf == NULL || num == 0) {
        return -128;
    }

    b = buf[0];
    for (i = 1; i < num; i++) {
        if (buf[i] > b) {
            b = buf[i];
        }
    }

    return b;
}

/*! delay for a while, in us resolusion.
 *  it's a cpu hog.
 */
static int8 delay(uint16 t)
{
    uint16 i;
    /* enlarge t to the us scale */
    /* 1 memory operation approx. equals to 0.125 us, so 8 such operations equals to 1 us */
    const uint8 ops_in_us = 8;
    t *= ops_in_us;
    while (t--) {
        i = t;
    }

    return MAC_OK;
}

/*! local function used to do the real active scan.
 *  @return the status of the scan.
 */
static int8 active_scan_engine(void)
{
    uint8 c;
    buf_t *buf;
    uint8 r;
    bool en_indir = false;
    uint8 dsn = mac_get_dsn();
    mtimer_t *timer;

    MAC_DEBUG("active_scan_engine.\n");
    buf = buf_alloc(true);
    if (buf == NULL) {
        r = MAC_TRANSACTION_OVERFLOW;
        goto active_scan_engine_err_process;
    }

    c = asa.chnls[asa.chnl_index++];
    rf_set_chnl(c);
    asa.pan_id_stored = rf_get_pan_id();
    rf_set_pan_id(BROADCAST_PAN_ID);

    MAC_DEBUG("active scan engine: the length of the beacon frame is %d.\n", buf->length);
    MAC_DEBUG("active scan engine: the frame to be sent is:\n");
    disp_bins(buf->ptr, buf->length);
    mac_gen_bcn_req(buf, dsn);
    r = mac_csmaca_tx(buf, dsn, en_indir);
    if (r != MAC_OK) {
        goto active_scan_engine_err_process;
    }

    timer = mac_mcb_get_timer();
    timer->func = active_scan_callback;
    timer->expire = aBaseSuperframeDuration * ((1 << asa.duration) + 1);
    timer_init(timer, active_scan_callback);
    timer_start(timer, timer->expire);

    buf_free(buf);
    return MAC_OK;

active_scan_engine_err_process:

    MAC_DEBUG("active_scan_engine: active_scan_engine_err_process.\n");
    buf_free(buf);
    return r;
}

/*! the callback funciton used to realize the active scan, which will be called
 *  periodically when active scan is performed.
 *  @param signum
 */  
static void active_scan_callback(int8 signum)
{
    MAC_DEBUG("active_scan_callback: \n");
    if (asa.chnl_index > (asa.scan_chnls.num-1)) {
        mac_scan_cfm_t cfm;

        cfm.scan_type = mac_active_scan_type;
        cfm.status = MAC_OK;
        mac_set_state(mac_idle);
        /* just signal that active scan is finished */
        mac_scan_cfm(&cfm);

        rf_set_pan_id(asa.pan_id_stored);
        return;
    }

    active_scan_engine();
    return;
}

/*! active scan interface, the real scan operation is performed in active_scan_engine()
 *  with the help of active_scan_callback(), which will trigger the active_scan_engine()
 *  periodically.
 *  @param req pointer to the structure of mac scan request.
 *  @return the status of the active scan.
 */
int8 mac_active_scan(mac_scan_req_t *req)
{
    /* initialize the active scan assistant structure: asa */
    asa.scan_chnls = req->scan_chnls;
    memcpy(asa.chnls, req->scan_chnls.chnls, req->scan_chnls.num);
    asa.scan_chnls.chnls = asa.chnls;
    asa.duration = req->scan_duration;
    asa.chnl_index = 0;
    asa.pan_id_stored = rf_get_pan_id();
    mac_set_state(mac_scanning);
    MAC_DEBUG("mac_active_scan: \n");
    MAC_DEBUG("mac_active_scan: the state of the mac is %d.\n", mac_get_state());
    MAC_DEBUG("mac_active_scan: asa.scan_chnls.num is: %d\n", asa.scan_chnls.num);

    return active_scan_engine();
}

/*! the MAC passive scan.
 *  @param req the pointer to the type of MAC scan request.
 *  @return return the result of this operatino.
 */
int8 mac_passive_scan(mac_scan_req_t *req)
{
    return MAC_OK;
}

/*! the MAC orphan scan.
 *  @param req the pointer to the type of MAC sccn request.
 *  @return return the result of this operation.
 */
int8 mac_orphan_scan(mac_scan_req_t *req)
{
    return MAC_OK;
}

